/*
 *  thread.cc, Copyright (c) 2016, mogujie.com
 *  All rights reserved
 *
 *  Created on: 2016年2月28日
 *      Author: yongxue@mogujie.com
 */

#include "thread.h"

namespace thread {

ThreadException::ThreadException(IN const string& msg, IN int sys_code) {
	string tmp(strerror(sys_code));
	set_msg(("Thread: " + msg + ": " + tmp));
	set_code(sys_code);
}

/*******************************************
 * 线程的控制操作
 */
void ThreadCtl::join() {

	if (pthread_self() == m_tid) {
		throw ThreadException("self thread can't join self.");
	}

	void* ignore = nullptr;
	if (pthread_join(m_tid, &ignore) != 0) {
		throw ThreadException("pthread_join error", errno);
	}
	return;
}


void ThreadCtl::detach() {

	if (pthread_self() == m_tid) {
		throw ThreadException("self thread can't detach self.");
	}

	if (pthread_detach(m_tid) != 0) {
		throw ThreadException("pthread_detach error", errno);
	}
	return;
}


void ThreadCtl::sleep(IN long millsecond) {
	struct timespec ts;
	ts.tv_sec = millsecond / 1000;
	ts.tv_nsec = millsecond % 1000;
	nanosleep(&ts, 0);
	return;
}

void ThreadCtl::yield() {
	sched_yield();
	return;
}

/*******************************************
 * 创建线程，启动线程
 * 创建、启动之前，需锁定
 */
ThreadCtl Thread::start() {
	// 加锁
	lock::AutoLockT<ThreadLock> sync(m_lock);

	if (m_running) {
		throw ThreadException("This thread is started");
	}

	// 创建线程
	int ret = pthread_create(&m_tid, 0,
							(void *(*)(void*))&threadEntry,
							(void*)this);
	if (0 != ret) {
		throw ThreadException("pthread_create error", ret);
	}

	// 解锁，设置等待wait，等待新thread的运行
	m_lock.wait();

	// 继续
	return ThreadCtl(m_tid);
}


/**
 * 线程执行的入口函数
 */
void Thread::threadEntry(IN void* void_ptr) {
	Thread* thread = (Thread*)void_ptr;

	thread->m_running = true;					// 标记线程开始了

	/**
	 * 必须使用作用域，满足lock的操作：加锁、signal、解锁
	 */
	{
		lock::AutoLockT<ThreadLock> sync(thread->m_lock);
		thread->m_lock.allNotify();			// 设置，析构时执行通知，然后解锁
	}

	try {
		/**
		 * 正常情况下，线程一直在runTask里面不会退出，除非线程将要结束
		 */
		thread->runTask();
	}
	catch(...) {
		thread->m_running = false;
		throw;
	}
	thread->m_running = false;

	return ;
}


}


